<?php
class ss_doctrine_table extends Doctrine_Table
{
	public $ss_association=false;
	static protected $HYDRATORS=array(Doctrine::HYDRATE_ARRAY,'jsonRef',Doctrine::HYDRATE_RECORD);
	static public $LIMIT=10;	static public $RELATION_MODE=2;
	const RELATIONS_NONE=0;
	const RELATIONS_KEYS=1;
	const RELATIONS_FULL=2;
    
    /**
     * Returns $foldClasses.
     *
     * @see ss_doctrine_table::$foldClasses
     */
	
    public function getFold() {
    	if(!isset($this->baseQuery)) $this->getBaseQuery();
        return $this->fold;
    }
    
	public static $apiExpose;
	protected $baseQuery=array();
	protected $fold=array('NestedSet'=>false,'class'=>array(),'field'=>array());
	
	public function __construct(){
		call_user_func_array(array('parent','__construct'),func_get_args());
		//ss_error::log($this->getClassnameToReturn(),'Class name');
	}
	
	public function query($query=null,$relationMode=null,$hydrationMode=null,$limit=null,$param=array(),$queryMethod='base'){
		//ss_error::log($queryMethod,'query QM');
		if(is_null($hydrationMode)) $hydrationMode=self::$RELATION_MODE;
		$_query=$this->detectQuery($query,$relationMode,$queryMethod);
		//ss_error::log($_query,'Query');
		//ss_error::log($_query->getSqlQuery(),'SQL');
		return $_query->getAll($param,self::$HYDRATORS[$hydrationMode],$limit);
	}
	
	public function queryOne($query,$relationMode=null,$hydrationMode=null,$limit=null,$param=array(),$queryMethod='base',$debug=false){
		ss_error::log($queryMethod,'queryOne QM');
		if(is_null($hydrationMode)) $hydrationMode=self::$RELATION_MODE;
		$_query=$this->detectQuery($query,$relationMode,$queryMethod);
		//ss_error::log($_query);
		if($debug) ss_error::log($_query->getSqlQuery(),'Debug SQL query');
		//die('here');
		return $_query->getOne($param,self::$HYDRATORS[$hydrationMode],$limit);
	}
	
	public function queryPrimary($query,$relationMode=null,$hydrationMode=null,$limit=null,$param=array(),$queryMethod='base'){
		//ss_error::log($queryMethod,'queryPrimary QM');
		if(is_null($hydrationMode)) $hydrationMode=self::$RELATION_MODE;
		$_query=$this->primaryQuery($query,$relationMode,$queryMethod);
		//ss_error::log($_query);
		return $_query->getOne($param,self::$HYDRATORS[$hydrationMode],$limit);
	}
	
	public function count($query=null,$queryMethod='base'){
		//ss_error::log($queryMethod,'count QM');
		return $this->detectQuery($query,1,$queryMethod)->getCount();
		$_query=$this->detectQuery($query,1);
		//ss_error::log($_query->getCountSqlQuery(),'Count SQL query');
		//die('here');
		return $_query->getCount();
	}
	
	public function exists($query=null){
		//if($query)
		return ($this->count($query)?true:false);
	}
	
	public function detectQuery($query=null,$relationMode=null,$queryMethod='base'){
		ss_error::log($queryMethod,'detectQuery QM');
		if(is_null($relationMode)) $relationMode=self::$RELATION_MODE;
		if(empty($query)){
			//ss_error::warn($query,'Empty');
			return $this->baseQuery($relationMode,$queryMethod);
			//return call_user_func(array($this,'append'.ucfirst($queryMethod).'JoinDql'))
		} elseif(is_array($query)){
			if(is_array(current($query))){
				return $this->dqlArrayQuery($query,$relationMode,$queryMethod);
			} else {
				return $this->arrayQuery($query,$relationMode,$queryMethod);
			}
		} else {
			return $this->jsonQuery($query,$relationMode,$queryMethod);
		}
	}
	
	public function arrayQuery($query=array(),$relationMode=null,$queryMethod='base'){
		//ss_error::log($queryMethod,'QM');
		$_base=call_user_func(array($this,'get'.ucfirst($queryMethod).'Query'),true,$relationMode,$queryMethod);
		array_walk($query,function(&$val,$key,$base){$val=(strpos($key,'.')?(array_key_exists($key,$base->sslookup)?$base->sslookup[$key]:$key):$key).'='.(is_numeric($val)||is_bool($val)?$val:"'{$val}'");},$_base);
		//ss_error::log($query,'Walked query');
		return $_base->addWhere(implode(' AND ',$query));
	}
	
	public function dqlQuery($query,$relationMode=null,$queryMethod='base'){
		return call_user_func(array($this,'get'.ucfirst($queryMethod).'Query'),true,$relationMode,$queryMethod)->parseDqlQuery($query);
		//return $this->getBaseQuery(true,$relationMode)->parseDqlQuery($query);
	}
	
	public function dqlArrayQuery($query=array(),$relationMode=null,$queryMethod='base'){
		//ss_error::log($query,'dqlArrayQuery');
		//$_base=$this->getBaseQuery(false,$relationMode,);
		$_base=call_user_func(array($this,'get'.ucfirst($queryMethod).'Query'),false,$relationMode,$queryMethod);
		return $this->queryToDQL(array_replace_recursive($_base['query'],$query));
	}
	
	public function jsonQuery($query,$relationMode=null,$queryMethod='base'){
		$_jsonQuery=new ss_json_query($query);
		return $this->dqlArrayQuery($_jsonQuery->getDql(),$relationMode,$queryMethod);
	}
	
	public function baseQuery($relationMode=null,$queryMethod='base'){
		return call_user_func(array($this,'get'.ucfirst($queryMethod).'Query'),true,$relationMode,$queryMethod);
		//return $this->getBaseQuery(true,$relationMode);
	}
	
	public function primaryQuery($prim,$relationMode=self::RELATIONS_KEYS,$queryMethod='base'){
		return $this->dqlArrayQuery($query=array('where'=>array($this->getIdentifier().' = '.$prim)),$relationMode,$queryMethod);
	}
	
	public function queryToDQL($query,$lookup=array()){
		//ss_error::log($query, 'Final DQL array');
		$_query=$this->createQuery();
		$_query->sslookup=$lookup;
		foreach($query as $_com=>$_seq){
			foreach($_seq as $_id=>$_part){
				if(is_array($_part)){
					call_user_func(array($_query,$_com),$_part[0],$_part[1]);
				} else {
					call_user_func(array($_query,$_com),$_part);
				}
			}
		}
		return $_query;
	}
	public function getActiveQuery($createQuery=false,$relationMode=null,$queryMethod='active'){
		$_query=$this->getBaseQuery(false,$relationMode,'active');
		if($this->hasColumn('active')){
			$_query['query']['addWhere']['active']='pri.active=true';
		}
		if($this->hasColumn('date_pub') && $this->hasColumn('date_rej')){
			//ss_error::warn('May have timed pub');
			if($this->hasColumn('timedpub')){
				//ss_error::warn('Active timed pub');
				$_query['query']['addWhere']['timedpub']='timedpub=false OR ((date_pub < NOW() OR date_pub IS NULL) AND (date_rej > NOW() OR date_rej IS NULL))';
			}
			$_query['query']['orderBy'][]='COALESCE(date_pub,created_at) DESC';
		}
		$this->appendActiveQuery($_query);
		//ss_error::info($_query, 'getActiveQuery');
		return $createQuery?$this->queryToDQL($_query['query'],$_query['lookup']):$_query;
	}
	
	public function getBaseQuery($createQuery=false,$relationMode=null,$queryMethod='base'){
		//ss_error::info($queryMethod.$relationMode, 'getBaseQuery');
		if(is_null($relationMode)) $relationMode=self::$RELATION_MODE;
		if(isset($this->baseQuery[$queryMethod.$relationMode])) return $createQuery?$this->queryToDQL($this->baseQuery[$queryMethod.$relationMode]['query'],$this->baseQuery[$queryMethod.$relationMode]['lookup']):$this->baseQuery[$queryMethod.$relationMode];
		$_tableName=$this->getClassnameToReturn();
		//ss_error::group('BaseQuery for '.$_tableName);
		$_query=array();
		$_query['addSelect'][]='pri.*';
		$_query['from'][]=$_tableName.' pri';
		if($this->hasTemplate('NestedSet')) $this->fold['NestedSet']=$_tableName;
		$_rcount=0;
		$_alookup=array();
		if($relationMode===0){
			if($this->hasTemplate('I18n')){
				$_alookup['pri.Translation']='trans';
				$_query['innerJoin']['trans']='pri.Translation trans'.call_user_func(array($this,'append'.ucfirst($queryMethod).'JoinDqlTrans'),'trans',$_alookup);
				$_query['addSelect']['trans']='trans.*';
			}
		} else {
			$_relations=$this->getRelations();
			foreach($_relations as $_name=>$_rel){
				//$_rcount++;
				$_table=$_rel->getTable();
				if($_table->ss_association) {
					//ss_error::warn($_rel->getAlias(),'Skipping');
					continue;
				}
				$_rname='r'.++$_rcount;
				$_class=$_rel->getClass();
				$_alias=$_rel->getAlias();
				$_local=$_rel->getLocal();
				$_foreign=$_rel->getForeign();
				if(is_a($_rel,'Doctrine_Relation_Association') || is_a($_rel,'Doctrine_Relation_ForeignKey')){
					//if(strpos($_rel->getClass(), 'Translation')) ss_error::log($_rel);
					$_id=$_table->getIdentifier();
					$_alookup['pri.'.$_alias]=$_rname;
					if($_alias=='Translation'){
						//ss_error::info($queryMethod,'Joining self translation');
						$_query['innerJoin'][$_rname]="pri.{$_name} {$_rname}".call_user_func(array($this,'append'.ucfirst($queryMethod).'JoinDqlTrans'),$_rname,$_alookup);
					} else {
						$_query['leftJoin'][$_rname]="pri.{$_name} {$_rname}".call_user_func(array($_table,'append'.ucfirst($queryMethod).'JoinDql'),$_rname,$_alookup);
					}
					
					if(in_array($_class,self::$apiExpose)){
						if($relationMode===2||is_array($_id)){
							$_query['addSelect'][$_rname]=$_rname.'.*';
							if($_table->hasTemplate('I18n')) {
								//ss_error::log($_rname,'Trans for');
								$_alookup["pri.{$_alias}.Translation"]='rt'.$_rcount;
								$_query['leftJoin']['rt'.$_rcount]="{$_rname}.Translation rt{$_rcount}".call_user_func(array($_table,'append'.ucfirst($queryMethod).'JoinDqlTrans'),'rt'.$_rcount,$_alookup);
								$_query['addSelect']['rt'.$_rcount]='rt'.$_rcount.'.*';
							} else {
								//ss_error::log($_alias.' has not I18n!');
							}
						} else {
							$_query['addSelect'][$_rname]=$_rname.'.'.$_id;
						}
						$this->fold['class'][$_class]=true;
					} else {
						$_query['addSelect'][$_rname]=$_rname.'.*';
					}
				} else {
					if(in_array($_class,self::$apiExpose)) {
						$this->fold['class'][$_class]=true;
						$this->fold['field'][$_rel->getLocal()]=array('class'=>$_class,'alias'=>$_alias);
					}
				}
			}
		}
		//die('here');
		//ss_error::log($_alookup,'lookup');
		//ss_error::log($_query,'query');
		$this->appendBaseQuery($_query,$_alookup);
		$this->baseQuery[$queryMethod.$relationMode]=array('query'=>$_query,'lookup'=>$_alookup);
		//ss_error::groupEnd();
		return $createQuery?$this->queryToDQL($_query,$_alookup):array('query'=>$_query,'lookup'=>$_alookup);
	}
	
	public function objToAlias($obj,$lookup){
		//$_base=substr($_obj,0,strrpos($obj,'.')-1);
		$_base=explode('.',$obj);
		$_attr=array_pop($_base);
		$_base=implode('.',$_base);
		if(array_key_exists($_key=$_base,$lookup)||array_key_exists($_key='pri.'.$_base,$lookup)||array_key_exists($_key='prit.'.$_base,$lookup)){
			return $lookup[$_key].'.'.$_attr;
		}
		return $obj;
	}
	
	protected function appendBaseQuery(&$query,$_alookup){
		//return $query;
	}
	
	protected function appendActiveQuery(&$query){
		//return $query;
	}
	
	public function appendBaseJoinDql($rname,$_alookup=array()){
		return '';
	}
	
	public function appendActiveJoinDql($rname,$_alookup=array()){
		return '';
	}
	
	public function appendBaseJoinDqlTrans($rname){
		return '';
	}
	
	public function appendActiveJoinDqlTrans($rname){
		return '';
	}
	
	public function setJsonData($query,$data){
		$_query=$this->primaryQuery($query,self::RELATIONS_FULL);
		$_record=$_query->getOne(array(),2);
		//$_record=$this->find($query);
		//ss_error::log($_record->Title->toArray());
		$data=$this->fromJsonData($data,$query);
		//ss_error::log($data,'In data');
		//ss_error::log($_record->toArray(),'Existing data');
		$_record->synchronizeWithArray($data,true);
		//ss_error::log($_record->toArray(),'Merged data');
		$_record->save();
		return $_record->toArray(true);
	}
	
	public function newJsonData($class,$data){
		$_record=new $class();
		$data=$this->fromJsonData($data);
		$_record->synchronizeWithArray($data,true);
		$_record->save();
		//ss_error::log($_record->synchronizeWithArray($data,true),'Sync');
		//ss_error::log($_record->save(),'Save');
		return $this->queryPrimary(implode('_',$_record->identifier()),self::RELATIONS_KEYS,1);
		return $_record->toArray(true);
	}
	
	protected function fromJsonData($data,$id=null){
		$_relations=$this->getRelations();
		//ss_error::log(array_keys($_relations),'Relations');
		foreach($_relations as $_name=>$_rel){
			if(array_key_exists($_name,$data)){
				//ss_error::log(get_class($_rel),$_name);
				if(is_array($data[$_name]) && count($data[$_name])>0){
					if(array_key_exists('$ref', $data[$_name])){
						$data[$_name]=$this->refToPrimary($data[$_name]['$ref']);
					} else {
						foreach($data[$_name] as $key=>$val){
							if(isset($val['$ref'])) $data[$_name][$key]=$this->refToPrimary($val['$ref']);
						}
					}
				}
			}
			if($_name=='Translation'  && isset($data['Translation']) && !is_null($id)){
				foreach($data['Translation'] as $key=>$val){
					$data['Translation'][$key][$this->getIdentifier()]=$id;
				}
			}
			if(is_a($_rel,'Doctrine_Relation_LocalKey') && !isset($data[$_rel->getLocal()])){
				//ss_error::log($_name,'Fixing relation');
				if(isset($data[$_name])) $data[$_rel->getLocal()]=$data[$_name];
				//unset($data[$_name]);
			}
		}
		if($this->hasTemplate('Timestampable')){
			unset($data['created_at'],$data['updated_at']);
		}
		return $data;
	}
	
	protected function refToPrimary($ref){
		$_ref=explode('/',$ref);
		$_primary=array_pop($_ref);
		$_table=Doctrine::getTable(array_pop($_ref));
		$_id=$_table->getIdentifier();
		if(strpos($_primary,'_')!==false && is_array($_id)){
		} else {
			return $_primary;
			return(array($_id=>$_primary));
		}
	}
}

class ss_doctrine_queryAdapter{
	private $_userQuery;
	
	public function __construct($query){
		$this->_userQuery=$query;
	}
	
}
?>